package com.ahearttogo.rpc.server;

import com.aharttogo.rpc.transport.RequestHandler;
import com.aharttogo.rpc.transport.TransportServer;
import com.ahearttogo.rpc.codec.Decoder;
import com.ahearttogo.rpc.codec.Encoder;
import com.ahearttogo.rpc.common.utils.ReflectionUtil;
import com.ahearttogo.rpc.proto.Request;
import com.ahearttogo.rpc.proto.Response;
import lombok.extern.slf4j.Slf4j;
import sun.misc.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author xiejialin
 * @date 2020/2/17 4:25 PM
 */
@Slf4j
public class RpcServer {

    private RpcServerConfig config;

    private TransportServer net;

    private Encoder encoder;

    private Decoder decoder;

    private ServiceManager serviceManager;

    private ServiceInvoker serviceInvoker;

    public RpcServer() {
        this.config = new RpcServerConfig();
        this.net = ReflectionUtil.newInstance(config.getTransportClass());
        this.net.init(config.getPort(),this.handler);
        this.encoder = ReflectionUtil.newInstance(config.getEncoderClass());
        this.decoder = ReflectionUtil.newInstance(config.getDecoderClass());
        this.serviceManager = new ServiceManager();
        this.serviceInvoker = new ServiceInvoker();
    }

    public <T> void register(Class<T> interfaceClass,T bean) {
        serviceManager.register(interfaceClass,bean);
    }

    public void start(){
        this.net.start();
    }

    public void stop(){
        this.net.stop();
    }

    private RequestHandler handler = new RequestHandler() {
        @Override
        public void onRequest(InputStream receive, OutputStream toResp) {
            Response resp = new Response();
            try {
                byte[] inBytes = IOUtils.readFully(receive,receive.available(),true);
                Request request = decoder.decode(inBytes,Request.class);
                log.info("get request: {}" , request);

                ServiceInstance serviceInstance = serviceManager.search(request);
                Object result = serviceInvoker.invoke(serviceInstance,request);
                resp.setData(result);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
                resp.setCode(1);
                resp.setMsg("RpcService got error:" + e.getClass().getName() + ":" + e.getMessage());
            }finally {
                byte[] outByts = encoder.encode(resp);
                try {
                    toResp.write(outByts);
                    log.info("response client");
                } catch (IOException e) {
                   log.error(e.getMessage(),e);
                }
            }
        }
    };


}
